package com.cyf.plc.plc

import com.cyf.library.util.loge
import com.cyf.library.util.logi
import com.cyf.plc.bean.PlcWriteAttribute
import com.cyf.plc.constant.Constants
import com.cyf.plc.model.PlcAttribute
import com.cyf.plc.model.PlcFault
import com.cyf.plc.plc.client.PlcClient
import com.cyf.library.thread.ThreadManager.startThread
import com.cyf.library.thread.ThreadManager.stopThreadExecutor
import com.pixplicity.easyprefs.library.Prefs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import java.util.*

object PlcExecutor {

    private const val PLC_IP_TAG = "plc_ip"

    //    private const val PLC_IP_ADDRESS = "192.168.1.152"
    private const val PLC_IP_ADDRESS = "192.168.124.10"

    fun getPlcIp(): String {
        return Prefs.getString(PLC_IP_TAG, PLC_IP_ADDRESS)
    }

    fun savePlcIp(plcIp: String) {
        Prefs.putString(PLC_IP_TAG, plcIp)
    }

    /**
     * 设置当前plc寄存器地址列表
     *
     * @return 地址信息列表
     */
    var plcAttributeList: List<PlcAttribute> = ArrayList()

    /**
     * 设置当前故障点列表
     */
    var plcFaultList: List<PlcFault> = ArrayList()

    /**
     * plc连接客户端，读写
     */
    private lateinit var plcClient: PlcClient

    /**
     * 初始化PLC客户端（使用线程池）
     *
     * @param ip               ip地址
     * @param startReadAddress 起始读取地址
     * @param bufferSize       buffer数组大小
     */
    fun initPlc(
        ip: String,
        startReadAddress: Int,
        bufferSize: Int
    ) {
        //初始化Plc客户端
        plcClient = PlcClient(ip, startReadAddress, bufferSize)
        //启动线程,一直在后台进程中进行读取
        startThread(plcClient, "Plc-client-$ip")
    }

    /**
     * 使用协程，启动监听plc通讯
     */
    fun initPlc2(
        ip: String,
        startReadAddress: Int,
        bufferSize: Int
    ) {

        //启动线程,一直在后台进程中进行读取
        GlobalScope.launch {
            withContext(Dispatchers.IO) {
                //初始化Plc客户端
                plcClient = PlcClient(ip, startReadAddress, bufferSize)
                plcClient.run()
            }
        }
    }

    /**
     * 关闭PLC客户端
     */
    fun closePlc() {
        plcClient.closeS7Client()
        stopThreadExecutor()
        "Close plc client.".loge()
    }

    /**
     *
     *写入值到plc，数值变化，等待变化之后异步处理
     */
    fun write(plcWriteAttribute: PlcWriteAttribute) {
        "Write:$plcWriteAttribute".logi()
        plcClient.putWriteBean(plcWriteAttribute)
    }

    /**
     * 读取plc地址
     *
     */
    fun read(readPlcAttribute: PlcAttribute) {
//        "Read:$readPlcAttribute".logi()
        synchronized(plcAttributeList) {
            for (plcAttribute in plcAttributeList) {
                if (plcAttribute.offSet == readPlcAttribute.offSet) {
                    //todo 寄存器数值有更新,通知界面
                    postPlcAttribute(plcAttribute)
                }
            }
        }
    }

    fun read(offset: Int, unit: String) {
        synchronized(plcAttributeList) {
            for (plcAttribute in plcAttributeList) {
                if (plcAttribute.offSet == offset && plcAttribute.addressUnit.equals(unit)) {
                    //todo 寄存器数值有更新,通知界面
                    postPlcAttribute(plcAttribute)
                }
            }
        }
    }

    /**
     * 更新
     */
    fun updatePlcSingleAddressList() {
        //同步更新plc读取地址列表
        synchronized(plcAttributeList) {
            //2.更新当前列表
            for (plcAttribute in plcAttributeList) {
                var shouldUpdate = false
                var readValue: Float
                when (plcAttribute.addressUnit) {
                    Constants.VW -> {
                        readValue = plcClient.GetShortAt(plcAttribute.offSet).toFloat()
                        if (readValue != plcAttribute.value) {
                            shouldUpdate = true
                            plcAttribute.value = readValue
                        }
                    }
                    Constants.VD -> {
                        readValue = plcClient.GetFloatAt(plcAttribute.offSet)
                        if (readValue != plcAttribute.value) {
                            shouldUpdate = true
                            plcAttribute.value = readValue
                        }
                    }
                    Constants.VB -> {
                        readValue = plcClient.GetByteAt(plcAttribute.offSet).toFloat()
                        if (readValue != plcAttribute.value) {
                            shouldUpdate = true
                            plcAttribute.value = readValue
                        }
                    }
                }
                if (shouldUpdate) {
                    //todo 寄存器数值有更新,通知界面
                    postPlcAttribute(plcAttribute)
                }
            }
        }
        //同步更新故障地址读取列表
        synchronized(plcFaultList) {
            //2.更新当前列表
            for (plcFault in plcFaultList) {
                var readValue: Float
                var shouldUpdate = false
                when (plcFault.addressUnit) {
                    Constants.VW -> {
                        readValue = plcClient.GetShortAt(plcFault.offSet).toFloat()
                        if (readValue != plcFault.faultStatus) {
                            shouldUpdate = true
                            plcFault.faultStatus = readValue
                        }
                    }
                    Constants.VB -> {
                        readValue = plcClient.GetByteAt(plcFault.offSet).toFloat()
                        if (readValue != plcFault.faultStatus) {
                            shouldUpdate = true
                            plcFault.faultStatus = readValue
                        }
                    }
                }
                if (shouldUpdate) {
                    //todo 故障有更新,通知界面
                    EventBus.getDefault().post(plcFault)
                }
            }
        }
    }

    /**
     * 通过Eventbus更新UI
     */
    private fun postPlcAttribute(plcAttribute: PlcAttribute) {
        "寄存器更新:$plcAttribute".logi()
        EventBus.getDefault().post(plcAttribute)
    }
}